from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.db import models

class OrderedModel(models.Model):
    order = models.PositiveIntegerField(editable=False)
    
    def save(self, **options):
        if not self.id:
            try:
                self.order = self.__class__.objects.all().order_by("-order")[0].order + 1
            except IndexError:
                self.order = 0
        super(OrderedModel, self).save(**options)
        
        
    def order_link(self):
        model_type_id = ContentType.objects.get_for_model(self.__class__).id
        model_id = self.id
        
        kwargs = {"direction": "top", "model_type_id": model_type_id, "model_id": model_id}
        url_top = reverse("admin-move", kwargs=kwargs)
        
        kwargs["direction"] = "up"
        url_up = reverse("admin-move", kwargs=kwargs)
        
        kwargs["direction"] = "down"
        url_down = reverse("admin-move", kwargs=kwargs)
        
        kwargs["direction"] = "bottom"
        url_bottom = reverse("admin-move", kwargs=kwargs)
        
        return '<a href="%s">top</a> | <a href="%s">up</a> | <a href="%s">down</a> | <a href="%s">bottom</a>' % (url_top, url_up, url_down, url_bottom)
    order_link.allow_tags = True
    order_link.short_description = 'Move'
    
    @staticmethod
    def move_top(model_type_id, model_id):
        try:
            ModelClass = ContentType.objects.get(id=model_type_id).model_class()
            
            top = ModelClass.objects.get(id=model_id)
            lower_models = ModelClass.objects.filter(order__lt=top.order)
            
            for m in lower_models:
                m.order = m.order + 1
                m.save()
            
            top.order = 0
            top.save()
        except IndexError:
            pass
        except ModelClass.DoesNotExist:
            pass
    
    @staticmethod
    def move_bottom(model_type_id, model_id):
        try:
            ModelClass = ContentType.objects.get(id=model_type_id).model_class()
            
            bottom = ModelClass.objects.get(id=model_id)
            higher_models = ModelClass.objects.filter(order__gt=bottom.order)
            
            for m in higher_models:
                m.order = m.order - 1
                m.save()
            
            bottom.order = ModelClass.objects.count() - 1
            bottom.save()
        except IndexError:
            pass
        except ModelClass.DoesNotExist:
            pass
            
    @staticmethod
    def move_down(model_type_id, model_id):
        try:
            ModelClass = ContentType.objects.get(id=model_type_id).model_class()
            
            lower_model = ModelClass.objects.get(id=model_id)
            higher_model = ModelClass.objects.filter(order__gt=lower_model.order).order_by('order')[0]
            
            lower_model.order, higher_model.order = higher_model.order, lower_model.order
            
            higher_model.save()
            lower_model.save()
        except IndexError:
            pass
        except ModelClass.DoesNotExist:
            pass
                
    @staticmethod
    def move_up(model_type_id, model_id):
        try:
            ModelClass = ContentType.objects.get(id=model_type_id).model_class()
            
            higher_model = ModelClass.objects.get(id=model_id)
            lower_model = ModelClass.objects.filter(order__lt=higher_model.order).order_by('-order')[0]
            
            lower_model.order, higher_model.order = higher_model.order, lower_model.order
            
            higher_model.save()
            lower_model.save()
        except IndexError:
            pass
        except ModelClass.DoesNotExist:
            pass
            
    class Meta:
        ordering = ["order"]
        abstract = True
    

class GroupedOrderedModel(models.Model):
    group_by = None
    order = models.PositiveIntegerField(editable=False)
    
    #def __init__(self, **options):
    #    try:
    #        pass#getattr(self, self.group_by)
    #    except IndexError:
    #        raise ValueError('Invalid field name %s' % self.group_by)
    #    
    #    super(GroupedOrderedModel, self).__init__(**options)
    
    def save(self, **options):
        if not self.id:
            try:
                grouped_by = {self.group_by: getattr(self, self.group_by)}
                self.order = self.__class__.objects.all().filter(**grouped_by).order_by("-order")[0].order + 1
            except IndexError:
                self.order = 0
        super(GroupedOrderedModel, self).save(**options)
    
    def order_link(self):
        model_type_id = ContentType.objects.get_for_model(self.__class__).id
        model_id = self.id
        
        kwargs = {"direction": "top", "model_type_id": model_type_id, "model_id": model_id}
        url_top = reverse("admin-move", kwargs=kwargs)
        
        kwargs["direction"] = "up"
        url_up = reverse("admin-move", kwargs=kwargs)
        
        kwargs["direction"] = "down"
        url_down = reverse("admin-move", kwargs=kwargs)
        
        kwargs["direction"] = "bottom"
        url_bottom = reverse("admin-move", kwargs=kwargs)
        
        return '<a href="%(top)s">top</a> | <a href="%(up)s">up</a> | <a href="%(down)s">down</a> | <a href="%(bottom)s">bottom</a>' % {
            'top': url_top,
            'up': url_up,
            'down': url_down,
            'bottom': url_bottom
        }
    order_link.allow_tags = True
    order_link.short_description = 'Move'
        
    @staticmethod
    def move_top(model_type_id, model_id):
        try:
            ModelClass = ContentType.objects.get(id=model_type_id).model_class()

            top = ModelClass.objects.get(id=model_id)
            grouped_by = {ModelClass.group_by: getattr(top, ModelClass.group_by)}
            lower_models = ModelClass.objects.filter(order__lt=top.order).filter(**grouped_by)
            
            for m in lower_models:
                m.order = m.order + 1
                m.save()
            
            top.order = 0
            top.save()
        except IndexError:
            pass
        except ModelClass.DoesNotExist:
            pass
    
    @staticmethod
    def move_bottom(model_type_id, model_id):
        try:
            ModelClass = ContentType.objects.get(id=model_type_id).model_class()

            bottom = ModelClass.objects.get(id=model_id)
            grouped_by = {ModelClass.group_by: getattr(bottom, ModelClass.group_by)}
            higher_models = ModelClass.objects.filter(order__gt=bottom.order).filter(**grouped_by)
            
            for m in higher_models:
                m.order = m.order - 1
                m.save()
            
            bottom.order = ModelClass.objects.filter(**grouped_by).count() - 1
            bottom.save()
            
            
        except IndexError:
            pass
        except ModelClass.DoesNotExist:
            pass
    
    @staticmethod
    def move_down(model_type_id, model_id):
        try:
            ModelClass = ContentType.objects.get(id=model_type_id).model_class()

            lower_model = ModelClass.objects.get(id=model_id)
            grouped_by = {ModelClass.group_by: getattr(lower_model, ModelClass.group_by)}
            higher_model = ModelClass.objects.filter(order__gt=lower_model.order).filter(**grouped_by).order_by('order')[0]
            
            lower_model.order, higher_model.order = higher_model.order, lower_model.order

            higher_model.save()
            lower_model.save()
        except IndexError:
            pass
        except ModelClass.DoesNotExist:
            pass
    
    @staticmethod
    def move_up(model_type_id, model_id):
        try:
            ModelClass = ContentType.objects.get(id=model_type_id).model_class()

            higher_model = ModelClass.objects.get(id=model_id)
            grouped_by = {ModelClass.group_by: getattr(higher_model, ModelClass.group_by)}
            lower_model = ModelClass.objects.filter(order__lt=higher_model.order).filter(**grouped_by).order_by('-order')[0]

            lower_model.order, higher_model.order = higher_model.order, lower_model.order

            higher_model.save()
            lower_model.save()
        except IndexError:
            pass
        except ModelClass.DoesNotExist:
            pass
    
    class Meta:
        ordering = ["order",]
        abstract = True